// Copyright (c) 2013  INRIA Sophia Antipolis -  Mediterranee (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 3 of the License,
// or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// 
//
// Author(s)     : Olivier Devillers
 
#include <CGAL/Cartesian.h>
#include <CGAL/CGAL_Ipelet_base.h> 
#include <CGAL/Object.h>

#include "include/CGAL_ipelets/pencils.h"


namespace CGAL_pencils{

typedef CGAL::Cartesian<double>                                               Kernel;
// --------------------------------------------------------------------

const std::string sublabel[] = {
  "Circle in pencil, orthogonal to circle", "Circle  orthogonal to three circles", "Help"
};

const std::string helpmsg[] = {
  "Draw the circle orthogonal to a circle (primary selection) in the pencil generated by two circles",
  "Draw the circle orthogonal to three circles"
};

class pencilIpelet 
  : public CGAL::Ipelet_base<Kernel,3> {
public:
  pencilIpelet() 
    :CGAL::Ipelet_base<Kernel,3>("Pencils of circles",sublabel,helpmsg){}
  void protected_run(int);
};
// --------------------------------------------------------------------

void pencilIpelet::protected_run(int fn)
{
  Circle_2 circ;     //constructed circle:
  
  if (fn==2) {
    show_help();
    return;
  } 
  
  Circle_2  c,c0,c1,c2;
  std::list<Point_2> pt_list,pt_list1;
  std::list<Circle_2> cir_list,cir_list1;


  int i=get_IpePage()->primarySelection(); 

  if (i<0) {
    print_error_message(("No mark or circle selected"));
    return;
  }

  read_one_active_object(get_IpePage()->object(i),CGAL::dispatch_or_drop_output<Point_2,Circle_2>(
       std::back_inserter(pt_list1),
       std::back_inserter(cir_list1))); 
  
  std::list<Point_2>::iterator it1=pt_list1.begin();
  std::list<Circle_2>::iterator cit1=cir_list1.begin();

  if (pt_list1.empty() && cir_list1.empty()){
  print_error_message(("Primary selection must be a mark or a circle"));
    return;
  }
  if (it1!=pt_list1.end())  {c=Circle_2(*it1,0);} else c=*cit1;

  Iso_rectangle_2 bbox=
  read_active_objects(
		      CGAL::dispatch_or_drop_output<Point_2,Circle_2>(
      std::back_inserter(pt_list),
      std::back_inserter(cir_list)
    )
  );

  std::list<Point_2>::iterator it=pt_list.begin();
  std::list<Circle_2>::iterator cit=cir_list.begin();


  // read c0
  if (it!=pt_list.end())  { c0=Circle_2(*it,0); ++it;}
  else if (cit!=cir_list.end())  { c0=*cit; ++cit;}
  else {print_error_message(("Not enough marks or circles selected")); return;}
  // read c1
  if (it!=pt_list.end())  { c1=Circle_2(*it,0); ++it;}
  else if (cit!=cir_list.end())  { c1=*cit; ++cit;}
  else {print_error_message(("Not enough marks or circles selected")); return;}
  // read c2
  if (it!=pt_list.end())  { c2=Circle_2(*it,0); ++it;}
  else if (cit!=cir_list.end())  { c2=*cit; ++cit;}
  else {print_error_message(("Not enough marks or circles selected")); return;}
  if ((it!=pt_list.end())||(cit!=cir_list.end()))
    {print_error_message(("Warning: more than three marks or circles selected"));}
  
  // c is the primary selection
  if (c==c1) c1=c0;
  if (c==c2) c2=c0;


  switch(fn){
  case 0:
    // Circle orthogonal to circle c in pencil generated by c1 c2
    circ = compute_circle_in_pencil<Kernel>(c,c1,c2);
    break;
  case 1:
    // Circle orthogonal to three circles
    circ = compute_circle_orthogonal<Kernel>(c,c1,c2);
    break;    
    
  }     //end of switch


  // detect degenerate case
  if (circ==Circle_2()){ 
    Kernel::Vector_2  v;
    if(fn==0)
      v=Kernel::Vector_2(
	  c2.center().y()-c1.center().y(),c2.center().x()-c1.center().x());
    else v=c2.center()-c1.center();
      Kernel::FT sqr_length= 1 / v.squared_length();
      double length = 600 * sqrt( sqr_length);
      v = Kernel::FT(length)*v;
      Point_2 q1=c.center()+ v;
      Point_2 q2=c.center()- v;
      print_error_message(
	 "degenerate case, circle is a line");
      Kernel::Segment_2 s(q1,q2);
      draw_in_ipe(s);
      return;
  }

  if (circ.squared_radius()>0){
    draw_in_ipe(circ);
  }else{
    print_error_message(("Computed circle is imaginary"));
  }
}
}

CGAL_IPELET(CGAL_pencils::pencilIpelet)
